#pragma once

#include "TimeSys.h"
#include "date.h"

namespace ns_Time
{
    class CommonTime : public TimeSys
    {
    public:
        using YearType = unsigned int;
        using MonthType = unsigned int;
        using DayType = unsigned int;
        using HourType = unsigned int;
        using MinuteType = unsigned int;
        using SecondType = double;

    public:
        CommonTime()
        {
        }

        CommonTime(YearType year, MonthType month, DayType day, HourType hour, MinuteType minute, SecondType second)
            : _year(year),
              _month(month),
              _day(day),
              _hour(hour),
              _minute(minute),
              _second(second)
        {
        }

        SecondCount SecondFromRefTime() const override
        {
            CommonTime *RefTime = GetRefTime();
            DayType gapDay = Date(_year, _month, _day) - Date(RefTime->_year, RefTime->_month, RefTime->_day);

            return static_cast<SecondCount>(gapDay * DaySecond + (_hour - RefTime->_hour) * HourSecond +
                                            (_minute - RefTime->_minute) * MinuteSecond + (_second - RefTime->_second));
        }

        CommonTime *TimeAfterSecond(SecondCount seconds) const override
        {
            int secondsInt;
            double secondsMilli;
            Mathf::SplitDouble(seconds, &secondsInt, &secondsMilli);

            DayType plusDays = secondsInt / DaySecond;
            secondsInt %= DaySecond;

            HourType plusHour = secondsInt / HourSecond;
            secondsInt %= HourSecond;

            MinuteType plusMinute = secondsInt / MinuteSecond;
            secondsInt %= MinuteSecond;

            SecondType newSecond = _second + secondsInt;
            MinuteType newMinute = _minute + plusMinute;
            HourType newHour = _hour + plusHour;

            if (newSecond > MinuteSecond)
            {
                newSecond -= MinuteSecond + secondsMilli;
                newMinute++;
            }

            if (newMinute > 60)
            {
                newMinute -= 60;
                newHour++;
            }

            if (newHour > 24)
            {
                newHour -= 24;
                plusDays++;
            }

            Date newDate = Date(_year, _month, _day) + plusDays;

            CommonTime *ans = new CommonTime(newDate._year, newDate._month, newDate._day, newHour, newMinute, newSecond);
            return ans;
        }

        CommonTime *GetRefTime() const override
        {
            static CommonTime refTime = CommonTime(1980, 1, 1, 0, 0, 0);
            return &refTime;
        }

        DayType GetDoy() const
        {
            return Date(_year, _month, _day) - Date(_year, 1, 1) + 1;
        }

        bool Decode(const std::string& inJson) override
        {
            Json::Value inValue;
			Json::Reader reader;
			bool parsingSuccessful = reader.parse(inJson, inValue);
			if (!(parsingSuccessful && inValue["Type"].asString() == "TimeSys"))
				return false;

            SecondCount seconds = inValue["SecondsFromRefTime"].asDouble();

            auto tmp = GetRefTime()->TimeAfterSecond(seconds);
            *this = *tmp;

            return true;
        }

    private:
        YearType _year;
        MonthType _month;
        DayType _day;
        HourType _hour;
        MinuteType _minute;
        SecondType _second;
    };
}
